﻿using SharpDepend;
using SharpDepend.Datatypes;
using System;
using ZquenceStudio3Host.Graphics;
using ZquenceStudio3Host.Graphics.Components;
using ZquenceStudio3Host.Graphics.Components.Interfaces;
using ZquenceStudio3Host.Graphics.EntityProperties;
using ZquenceStudio3Host.Graphics.GUI;
using ZquenceStudio3Host.Graphics.Manager;

namespace DefaultDspPlugins.UI.Common.Modulizer.Engine.GUIv2.Controls
{
    public class Dial : BaseModulizerControl
    {
        private const double TO_RADIAN = Math.PI / 180;

        private ShapesManager mShapes = new ShapesManager();
        private double mAngleValue;
        private int mIndicators = 16;
        private Color mIndicatorColor = Color.Black;
        private Color mPointerColor = Color.Black;
        private Color mDialColor = Color.Gray;
        private Label mLabel;
        private Label mLabel2;

        public int IndicatorStartAngle { get; set; }

        public int IndicatorEndAngle { get; set; }

        public bool AsInteger { get; internal set; }

        public event EventOnValueChanged OnValueChanged;

        public Dial(BasicGuiManager engineCore) : base(engineCore)
        {
            mShapes.SetGraphicsDevice(engineCore.Display.GraphicsDevice);

            IndicatorStartAngle = 45;
            IndicatorEndAngle = 360 - 45;

            Events.OnMousePressing2 += Events_OnMousePressing;
            Events.OnMouseMoving += Events_OnMouseMoving;
            Events.OnMouseUnpressing += Events_OnMouseUnpressing;

            mLabel = new Label(engineCore, true);
            mLabel.CenterText = true;
            mLabel.AutoSize = true;
            mLabel2 = new Label(engineCore, true);
            mLabel2.CenterText = true;
            mLabel2.AutoSize = true;
        }

        protected override void OnValueUpdated()
        {
            const double pi360 = Math.PI * 2;

            mAngleValue = pi360 * 1.0 / (MaxValue - MinValue) * (Value - MinValue);

            mLabel.Text = CurrentParameter == null ? string.Empty : CurrentParameter.Label;
            mLabel2.Text = ((int)(Value * 100) / 100.0).ToString();
        }

        protected override void OnParameterChanged()
        {
            mInternallyValue = Value;

            AsInteger = CurrentParameter.IsInteger;

            OnValueUpdated();
        }

        public override void AfterRender(IGraphicsDevice canvas, Parent parent)
        {
            base.AfterRender(canvas, parent);
        }

        public override void Render(IGraphicsDevice canvas, Parent parent)
        {
            if (Visible)
            {
                base.Render(canvas, parent);

                mShapes.SetGraphicsDevice(canvas);
                mShapes.BeginDraw();

                double realX = parent.X + ActualX;
                double realY = parent.Y + ActualY;
                double realWidth = ActualWidth;
                double realHeight = ActualHeight;

                double radian = realHeight * 0.5;
                double buttonRadianMultiply = radian * 0.6;
                double centerX = realX + realWidth * 0.5;
                double centerY = realY + realHeight * 0.5;
                double shdCenterX = realX + realWidth * 0.5 + radian * 0.1;
                double shdCenterY = realY + realHeight * 0.5 + radian * 0.1;
                double indicatorRangeRadian = 1.0 / 360.0 * (IndicatorEndAngle - IndicatorStartAngle);

                double label1Height = ActualHeight - buttonRadianMultiply * 2.8;
                double label2Height = ActualHeight - buttonRadianMultiply * 2.7;
                double label2Y = realY + ActualHeight - label2Height * 0.9;

                // Draw background.
                canvas.Draw(new Vector4(realX + 1, realY + 1, realX + realWidth - 2, realY + RealHeight - 2), new Color(60, 34, 34, 34));
                canvas.Draw(new Vector4(realX + 1, realY + 1, realX + realWidth - 2, realY + label2Height - 2), new Color(60, 34, 64, 127));
                canvas.Draw(new Vector4(realX + 1, label2Y, realX + realWidth - 2, label2Y + label2Height - 2), new Color(40, 34, 34, 54));

                // Draw indicators.
                int indicators = mIndicators;
                for (int i = 0; i < indicators + 1; i++)
                {
                    double iPos = 360.0 / indicators * i;
                    double ind_angle = IndicatorStartAngle * TO_RADIAN + (Math.PI * 2) / 360 * iPos * indicatorRangeRadian;
                    double ind_angleX = -Math.Sin(ind_angle);
                    double ind_angleY = Math.Cos(ind_angle);
                    double ind_ptrX0 = centerX + ind_angleX * buttonRadianMultiply * 1.1;
                    double ind_ptrY0 = centerY + ind_angleY * buttonRadianMultiply * 1.1;
                    double ind_ptrX1 = centerX + ind_angleX * buttonRadianMultiply * 1.3;
                    double ind_ptrY1 = centerY + ind_angleY * buttonRadianMultiply * 1.3;
                    mShapes.DrawLine(ind_ptrX0, ind_ptrY0, ind_ptrX1, ind_ptrY1, mIndicatorColor);
                }

                // Draw shadow.
                Color shadow = new Color(180, 0, 0, 0);
                mShapes.DrawSimpleCircle(shdCenterX, shdCenterY, radian, shadow, Color.Transparent);

                // Draw base dircle.
                mShapes.DrawFilledCircle(centerX, centerY, buttonRadianMultiply, mDialColor, Color.Black);

                // Draw dial pointer.
                double angleX = -Math.Sin(IndicatorStartAngle * TO_RADIAN + mAngleValue * indicatorRangeRadian);
                double angleY = Math.Cos(IndicatorStartAngle * TO_RADIAN + mAngleValue * indicatorRangeRadian);
                double ptrX0 = centerX + angleX * buttonRadianMultiply * 0.4;
                double ptrY0 = centerY + angleY * buttonRadianMultiply * 0.4;
                double ptrX1 = centerX + angleX * buttonRadianMultiply * 0.9;
                double ptrY1 = centerY + angleY * buttonRadianMultiply * 0.9;
                mShapes.DrawLine(ptrX0, ptrY0, ptrX1, ptrY1, mPointerColor);

                mShapes.EndDraw();

                // Display label.
                mLabel.Width = ActualWidth;
                mLabel.Height = label1Height;
                mLabel.X = ActualX;
                mLabel.Y = ActualY;
                mLabel.Render(canvas, parent);

                // Value.
                mLabel2.Width = ActualWidth;
                mLabel2.Height = label2Height;
                mLabel2.X = ActualX;
                mLabel2.Y = label2Y - parent.Y;
                mLabel2.Render(canvas, parent);
            }
        }

        private double mMouseDownX;
        private double mInternallyValue;

        private bool Events_OnMousePressing(object sender, double x, double y, int id)
        {
            MouseBeginDown();

            mMouseDownX = x;

            MouseEndDown();

            return true;
        }

        private void Events_OnMouseMoving(object sender, double x, double y, int id)
        {
            MouseBeginMove();

            double newPos = x - mMouseDownX;
            double newValue = (MaxValue - MinValue) / 300.0 * newPos;

            if (mInternallyValue + newValue > MaxValue)
            {
                mInternallyValue = MaxValue;
            }
            else if(mInternallyValue + newValue < MinValue)
            {
                mInternallyValue = MinValue;
            }
            else
            {
                mInternallyValue += newValue;
            }

            if (AsInteger)
            {
                Value = (int)(mInternallyValue + 0.5);
            }
            else
            {
                Value = mInternallyValue;
            }

            mMouseDownX = x;

            MouseEndMove();
        }

        private void Events_OnMouseUnpressing(object sender, double x, double y, int id)
        {
            MouseUp();
        }

        static Dial()
        {
            DefinedProperties.AddProperty("Indicators", new EntityInteger<Dial>(
                (Dial obj, int value) =>
                {
                    obj.mIndicators = value;
                },
                (Dial obj) =>
                {
                    return obj.mIndicators;
                }));
            DefinedProperties.AddProperty("Indicators color", new EntityColor<Dial>(
                (Dial obj, Color value) =>
                {
                    obj.mIndicatorColor = value;
                },
                (Dial obj) =>
                {
                    return obj.mIndicatorColor;
                }));
            DefinedProperties.AddProperty("Pointer color", new EntityColor<Dial>(
                (Dial obj, Color value) =>
                {
                    obj.mPointerColor = value;
                },
                (Dial obj) =>
                {
                    return obj.mPointerColor;
                }));
            DefinedProperties.AddProperty("Dial color", new EntityColor<Dial>(
                (Dial obj, Color value) =>
                {
                    obj.mDialColor = value;
                },
                (Dial obj) =>
                {
                    return obj.mDialColor;
                }));
            //DefinedProperties.AddProperty("Internal", new EntityInteger<Dial>(
            //    (Dial obj, int value) =>
            //    {
            //        obj.mIndicators = value;
            //    },
            //    (Dial obj) =>
            //    {
            //        return obj.mIndicators;
            //    }));
        }
    }
}
